<?php
namespace app\common\services;

use app\common\model\AttributionTrack;
use app\common\model\AttributionEvent;
use app\common\model\AttributionInstall;
use app\common\model\ChannelStats;
use support\Db;
use support\Log;
use support\Redis;

/**
 * 归因服务
 * 处理设备归因、参数传递、安装追踪等核心业务逻辑
 */
class AttributionService
{
    /**
     * 归因窗口期 (默认7天)
     */
    const ATTRIBUTION_WINDOW = 604800; // 7 days in seconds
    
    /**
     * 设备指纹相似度阈值
     */
    const FINGERPRINT_SIMILARITY_THRESHOLD = 0.85;
    
    /**
     * 归因置信度阈值
     */
    const ATTRIBUTION_CONFIDENCE_THRESHOLD = 0.7;

    /**
     * 追踪设备信息
     */
    public function trackDevice($appkey, $fingerprint, $deviceInfo, $customParams = [], $sessionId = '')
    {
        // 生成追踪ID
        $trackingId = $this->generateTrackingId($appkey, $fingerprint);
        
        // 创建新的追踪记录
        $trackData = [
            'tracking_id' => $trackingId,
            'appkey' => $appkey,
            'fingerprint' => $fingerprint,
            'session_id' => $sessionId,
            'device_info' => json_encode($deviceInfo),
            'custom_params' => json_encode($customParams),
            'ip_address' => $this->getClientIp(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'created_at' => date('Y-m-d H:i:s'),
            'expire_time' => date('Y-m-d H:i:s', time() + self::ATTRIBUTION_WINDOW)
        ];
        
        // 保存到数据库
        $trackModel = new AttributionTrack();
        $trackModel->insert($trackData);
        
        // 缓存设备指纹信息
        $this->cacheDeviceFingerprint($appkey, $fingerprint, $deviceInfo);
        
        // 更新渠道统计
        if (isset($customParams['channel'])) {
            $this->updateChannelStats($appkey, $customParams['channel'], 'click');
        }
        
        return [
            'tracking_id' => $trackingId,
            'session_id' => $sessionId ?: $this->generateSessionId(),
            'expire_time' => $trackData['expire_time'],
            'attribution_window' => self::ATTRIBUTION_WINDOW
        ];
    }

    /**
     * 归因安装
     */
    public function attributeInstall($appkey, $fingerprint, $deviceId, $installTime)
    {
        // 查找归因窗口期内的设备追踪记录
        $windowStart = date('Y-m-d H:i:s', $installTime - self::ATTRIBUTION_WINDOW);
        
        $trackModel = new AttributionTrack();
        $trackingRecords = $trackModel->where([
            'appkey' => $appkey,
            'fingerprint' => $fingerprint,
            ['created_at', '>=', $windowStart]
        ])->orderBy('created_at', 'desc')->select();
        
        if (empty($trackingRecords)) {
            return ['success' => false, 'reason' => 'No tracking data found'];
        }
        
        // 执行归因算法
        $attributionResult = $this->executeAttributionAlgorithm($trackingRecords, $deviceId, $installTime);
        
        if ($attributionResult['success']) {
            // 创建安装记录
            $installData = [
                'install_id' => $this->generateInstallId($appkey, $deviceId),
                'appkey' => $appkey,
                'device_id' => $deviceId,
                'fingerprint' => $fingerprint,
                'tracking_id' => $attributionResult['tracking_id'],
                'channel' => $attributionResult['channel'],
                'match_type' => $attributionResult['match_type'],
                'confidence' => $attributionResult['confidence'],
                'install_time' => date('Y-m-d H:i:s', $installTime),
                'attributed_at' => date('Y-m-d H:i:s'),
                'custom_params' => json_encode($attributionResult['custom_params'])
            ];
            
            $installModel = new AttributionInstall();
            $installModel->insert($installData);
            
            // 更新渠道统计
            if ($attributionResult['channel']) {
                $this->updateChannelStats($appkey, $attributionResult['channel'], 'install');
            }
            
            // 清理过期的追踪数据
            $this->cleanupExpiredTrackingData($appkey, $fingerprint);
        }
        
        return $attributionResult;
    }

    /**
     * 追踪事件
     */
    public function trackEvent($appkey, $fingerprint, $eventType, $eventData, $sessionId = '')
    {
        // 查找对应的安装记录
        $installModel = new AttributionInstall();
        $installRecord = $installModel->where([
            'appkey' => $appkey,
            'fingerprint' => $fingerprint
        ])->orderBy('install_time', 'desc')->find();
        
        // 生成事件ID
        $eventId = $this->generateEventId($appkey, $fingerprint, $eventType);
        
        // 创建事件记录
        $eventData = [
            'event_id' => $eventId,
            'appkey' => $appkey,
            'fingerprint' => $fingerprint,
            'install_id' => $installRecord ? $installRecord['install_id'] : null,
            'event_type' => $eventType,
            'event_data' => json_encode($eventData),
            'session_id' => $sessionId,
            'created_at' => date('Y-m-d H:i:s')
        ];
        
        $eventModel = new AttributionEvent();
        $eventModel->insert($eventData);
        
        // 更新渠道统计
        if ($installRecord && $installRecord['channel']) {
            $this->updateChannelStats($appkey, $installRecord['channel'], $eventType);
        }
        
        return [
            'event_id' => $eventId,
            'processed' => true,
            'timestamp' => time()
        ];
    }

    /**
     * 获取实时统计
     */
    public function getRealtimeStats($appkey, $timeRange = '24h')
    {
        // 解析时间范围
        $timeRanges = [
            '1h' => 3600,
            '24h' => 86400,
            '7d' => 604800,
            '30d' => 2592000
        ];
        
        $seconds = $timeRanges[$timeRange] ?? 86400;
        $startTime = date('Y-m-d H:i:s', time() - $seconds);
        
        // 获取统计数据
        $stats = [];
        
        // 点击量统计
        $trackModel = new AttributionTrack();
        $stats['clicks'] = $trackModel->where([
            'appkey' => $appkey,
            ['created_at', '>=', $startTime]
        ])->count();
        
        // 安装量统计
        $installModel = new AttributionInstall();
        $stats['installs'] = $installModel->where([
            'appkey' => $appkey,
            ['install_time', '>=', $startTime]
        ])->count();
        
        // 事件统计
        $eventModel = new AttributionEvent();
        $eventStats = $eventModel->where([
            'appkey' => $appkey,
            ['created_at', '>=', $startTime]
        ])->groupBy('event_type')->selectRaw('event_type, COUNT(*) as count')->get();
        
        $stats['events'] = [];
        foreach ($eventStats as $event) {
            $stats['events'][$event['event_type']] = $event['count'];
        }
        
        // 转化率
        $stats['conversion_rate'] = $stats['clicks'] > 0 
            ? round(($stats['installs'] / $stats['clicks']) * 100, 2) 
            : 0;
        
        // 渠道分布
        $channelStats = $installModel->where([
            'appkey' => $appkey,
            ['install_time', '>=', $startTime]
        ])->groupBy('channel')->selectRaw('channel, COUNT(*) as count')->get();
        
        $stats['channels'] = [];
        foreach ($channelStats as $channel) {
            $stats['channels'][$channel['channel']] = $channel['count'];
        }
        
        return $stats;
    }

    /**
     * 获取渠道分析
     */
    public function getChannelAnalytics($appkey, $startDate, $endDate, $channels = [])
    {
        // 基础查询条件
        $where = [
            'appkey' => $appkey,
            ['install_time', '>=', $startDate . ' 00:00:00'],
            ['install_time', '<=', $endDate . ' 23:59:59']
        ];
        
        if (!empty($channels)) {
            $where[] = ['channel', 'IN', $channels];
        }
        
        // 获取渠道数据
        $installModel = new AttributionInstall();
        $channelData = $installModel->where($where)
            ->groupBy('channel')
            ->selectRaw('
                channel,
                COUNT(*) as installs,
                AVG(confidence) as avg_confidence,
                COUNT(DISTINCT DATE(install_time)) as active_days,
                COUNT(CASE WHEN match_type = "exact" THEN 1 END) as exact_matches,
                COUNT(CASE WHEN match_type = "fuzzy" THEN 1 END) as fuzzy_matches
            ')
            ->get();
        
        $analytics = [];
        foreach ($channelData as $data) {
            $channel = $data['channel'];
            
            // 获取该渠道的点击数据
            $clickCount = $this->getChannelClickCount($appkey, $channel, $startDate, $endDate);
            
            // 获取事件数据
            $eventStats = $this->getChannelEventStats($appkey, $channel, $startDate, $endDate);
            
            $analytics[$channel] = [
                'clicks' => $clickCount,
                'installs' => $data['installs'],
                'conversion_rate' => $clickCount > 0 ? round(($data['installs'] / $clickCount) * 100, 2) : 0,
                'avg_confidence' => round($data['avg_confidence'] * 100, 2),
                'active_days' => $data['active_days'],
                'exact_matches' => $data['exact_matches'],
                'fuzzy_matches' => $data['fuzzy_matches'],
                'events' => $eventStats,
                'quality_score' => $this->calculateChannelQualityScore($data, $eventStats)
            ];
        }
        
        return $analytics;
    }

    /**
     * 验证归因结果
     */
    public function validateAttribution($appkey, $fingerprint, $deviceId)
    {
        // 查找安装记录
        $installModel = new AttributionInstall();
        $installRecord = $installModel->where([
            'appkey' => $appkey,
            'fingerprint' => $fingerprint
        ])->orderBy('install_time', 'desc')->find();
        
        if (!$installRecord) {
            return ['valid' => false, 'reason' => 'No installation record found'];
        }
        
        // 查找追踪记录
        $trackModel = new AttributionTrack();
        $trackingRecord = $trackModel->where([
            'tracking_id' => $installRecord['tracking_id']
        ])->find();
        
        if (!$trackingRecord) {
            return ['valid' => false, 'reason' => 'No tracking record found'];
        }
        
        // 验证归因逻辑
        $validation = [
            'valid' => true,
            'install_id' => $installRecord['install_id'],
            'tracking_id' => $installRecord['tracking_id'],
            'channel' => $installRecord['channel'],
            'match_type' => $installRecord['match_type'],
            'confidence' => $installRecord['confidence'],
            'time_gap' => strtotime($installRecord['install_time']) - strtotime($trackingRecord['created_at']),
            'fingerprint_match' => $installRecord['fingerprint'] === $fingerprint,
            'attribution_window_valid' => strtotime($installRecord['install_time']) - strtotime($trackingRecord['created_at']) <= self::ATTRIBUTION_WINDOW,
            'validation_checks' => []
        ];
        
        // 详细验证检查
        if ($validation['time_gap'] < 0) {
            $validation['valid'] = false;
            $validation['validation_checks'][] = 'Install time before tracking time';
        }
        
        if (!$validation['attribution_window_valid']) {
            $validation['valid'] = false;
            $validation['validation_checks'][] = 'Outside attribution window';
        }
        
        if ($installRecord['confidence'] < self::ATTRIBUTION_CONFIDENCE_THRESHOLD) {
            $validation['validation_checks'][] = 'Low confidence score';
        }
        
        return $validation;
    }

    /**
     * 执行归因算法
     */
    private function executeAttributionAlgorithm($trackingRecords, $deviceId, $installTime)
    {
        // 精确匹配
        $exactMatch = $this->findExactMatch($trackingRecords, $deviceId);
        if ($exactMatch) {
            return [
                'success' => true,
                'tracking_id' => $exactMatch['tracking_id'],
                'channel' => $this->extractChannelFromParams($exactMatch['custom_params']),
                'match_type' => 'exact',
                'confidence' => 1.0,
                'custom_params' => json_decode($exactMatch['custom_params'], true)
            ];
        }
        
        // 模糊匹配
        $fuzzyMatch = $this->findFuzzyMatch($trackingRecords, $deviceId, $installTime);
        if ($fuzzyMatch) {
            return [
                'success' => true,
                'tracking_id' => $fuzzyMatch['tracking_id'],
                'channel' => $this->extractChannelFromParams($fuzzyMatch['custom_params']),
                'match_type' => 'fuzzy',
                'confidence' => $fuzzyMatch['confidence'],
                'custom_params' => json_decode($fuzzyMatch['custom_params'], true)
            ];
        }
        
        return ['success' => false, 'reason' => 'No matching tracking data found'];
    }

    /**
     * 精确匹配
     */
    private function findExactMatch($trackingRecords, $deviceId)
    {
        foreach ($trackingRecords as $record) {
            $deviceInfo = json_decode($record['device_info'], true);
            if (isset($deviceInfo['device_id']) && $deviceInfo['device_id'] === $deviceId) {
                return $record;
            }
        }
        return null;
    }

    /**
     * 模糊匹配
     */
    private function findFuzzyMatch($trackingRecords, $deviceId, $installTime)
    {
        $bestMatch = null;
        $bestScore = 0;
        
        foreach ($trackingRecords as $record) {
            $score = $this->calculateMatchScore($record, $deviceId, $installTime);
            if ($score > $bestScore && $score >= self::FINGERPRINT_SIMILARITY_THRESHOLD) {
                $bestScore = $score;
                $bestMatch = $record;
                $bestMatch['confidence'] = $score;
            }
        }
        
        return $bestMatch;
    }

    /**
     * 计算匹配分数
     */
    private function calculateMatchScore($trackingRecord, $deviceId, $installTime)
    {
        $score = 0;
        $deviceInfo = json_decode($trackingRecord['device_info'], true);
        
        // 时间衰减因子
        $timeGap = abs($installTime - strtotime($trackingRecord['created_at']));
        $timeDecay = exp(-$timeGap / (self::ATTRIBUTION_WINDOW * 2)); // 半衰期为窗口期的一半
        
        // 设备指纹相似度
        $fingerprintScore = $this->calculateFingerprintSimilarity($deviceInfo, $deviceId);
        
        // IP地址一致性
        $ipScore = $this->checkIpConsistency($trackingRecord);
        
        // 综合评分
        $score = ($fingerprintScore * 0.6 + $ipScore * 0.4) * $timeDecay;
        
        return $score;
    }

    /**
     * 计算设备指纹相似度
     */
    private function calculateFingerprintSimilarity($deviceInfo, $deviceId)
    {
        // 简化的相似度计算
        // 实际实现中应该包含更复杂的算法
        $similarity = 0;
        
        if (isset($deviceInfo['device_id'])) {
            // 计算设备ID的相似度
            similar_text($deviceInfo['device_id'], $deviceId, $similarity);
            $similarity = $similarity / 100;
        }
        
        return $similarity;
    }

    /**
     * 检查IP一致性
     */
    private function checkIpConsistency($trackingRecord)
    {
        // 检查当前IP是否与追踪时的IP一致
        $currentIp = $this->getClientIp();
        return $currentIp === $trackingRecord['ip_address'] ? 1.0 : 0.5;
    }

    /**
     * 提取渠道信息
     */
    private function extractChannelFromParams($customParamsJson)
    {
        $params = json_decode($customParamsJson, true);
        return $params['channel'] ?? 'organic';
    }

    /**
     * 缓存设备指纹
     */
    private function cacheDeviceFingerprint($appkey, $fingerprint, $deviceInfo)
    {
        $cacheKey = "device_fingerprint:{$appkey}:{$fingerprint}";
        $cacheData = [
            'device_info' => $deviceInfo,
            'last_seen' => time(),
            'hit_count' => 1
        ];
        
        // 检查是否已存在
        $existing = Redis::get($cacheKey);
        if ($existing) {
            $existingData = json_decode($existing, true);
            $cacheData['hit_count'] = $existingData['hit_count'] + 1;
        }
        
        Redis::setex($cacheKey, 86400, json_encode($cacheData)); // 缓存24小时
    }

    /**
     * 更新渠道统计
     */
    private function updateChannelStats($appkey, $channel, $metric)
    {
        $date = date('Y-m-d');
        $cacheKey = "channel_stats:{$appkey}:{$channel}:{$date}";
        
        // 使用Redis进行实时统计
        Redis::hincrby($cacheKey, $metric, 1);
        Redis::expire($cacheKey, 86400 * 30); // 保留30天
        
        // 定期同步到数据库
        if (rand(1, 100) === 1) { // 1%概率同步
            $this->syncChannelStatsToDatabase($appkey, $channel, $date);
        }
    }

    /**
     * 同步渠道统计到数据库
     */
    private function syncChannelStatsToDatabase($appkey, $channel, $date)
    {
        $cacheKey = "channel_stats:{$appkey}:{$channel}:{$date}";
        $stats = Redis::hgetall($cacheKey);
        
        if (empty($stats)) {
            return;
        }
        
        $channelStatsModel = new ChannelStats();
        $existing = $channelStatsModel->where([
            'appkey' => $appkey,
            'channel' => $channel,
            'stat_date' => $date
        ])->find();
        
        if ($existing) {
            $existing->update([
                'clicks' => $stats['click'] ?? 0,
                'installs' => $stats['install'] ?? 0,
                'events' => json_encode($stats),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        } else {
            $channelStatsModel->insert([
                'appkey' => $appkey,
                'channel' => $channel,
                'stat_date' => $date,
                'clicks' => $stats['click'] ?? 0,
                'installs' => $stats['install'] ?? 0,
                'events' => json_encode($stats),
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s')
            ]);
        }
    }

    /**
     * 清理过期追踪数据
     */
    private function cleanupExpiredTrackingData($appkey, $fingerprint)
    {
        $expireTime = date('Y-m-d H:i:s', time() - self::ATTRIBUTION_WINDOW);
        
        $trackModel = new AttributionTrack();
        $trackModel->where([
            'appkey' => $appkey,
            'fingerprint' => $fingerprint,
            ['created_at', '<', $expireTime]
        ])->delete();
    }

    /**
     * 获取渠道点击数
     */
    private function getChannelClickCount($appkey, $channel, $startDate, $endDate)
    {
        $trackModel = new AttributionTrack();
        return $trackModel->where([
            'appkey' => $appkey,
            ['created_at', '>=', $startDate . ' 00:00:00'],
            ['created_at', '<=', $endDate . ' 23:59:59']
        ])->whereRaw("JSON_EXTRACT(custom_params, '$.channel') = ?", [$channel])->count();
    }

    /**
     * 获取渠道事件统计
     */
    private function getChannelEventStats($appkey, $channel, $startDate, $endDate)
    {
        // 获取该渠道的安装ID列表
        $installModel = new AttributionInstall();
        $installIds = $installModel->where([
            'appkey' => $appkey,
            'channel' => $channel,
            ['install_time', '>=', $startDate . ' 00:00:00'],
            ['install_time', '<=', $endDate . ' 23:59:59']
        ])->column('install_id');
        
        if (empty($installIds)) {
            return [];
        }
        
        // 获取事件统计
        $eventModel = new AttributionEvent();
        $eventStats = $eventModel->where([
            'appkey' => $appkey
        ])->whereIn('install_id', $installIds)
          ->groupBy('event_type')
          ->selectRaw('event_type, COUNT(*) as count')
          ->get();
        
        $stats = [];
        foreach ($eventStats as $event) {
            $stats[$event['event_type']] = $event['count'];
        }
        
        return $stats;
    }

    /**
     * 计算渠道质量分数
     */
    private function calculateChannelQualityScore($channelData, $eventStats)
    {
        $score = 0;
        
        // 转化率权重 40%
        $conversionRate = $channelData['clicks'] > 0 ? $channelData['installs'] / $channelData['clicks'] : 0;
        $score += $conversionRate * 40;
        
        // 归因准确度权重 30%
        $accuracyScore = $channelData['avg_confidence'] * 30;
        $score += $accuracyScore;
        
        // 事件活跃度权重 30%
        $totalEvents = array_sum($eventStats);
        $installs = $channelData['installs'];
        $activityScore = $installs > 0 ? min(($totalEvents / $installs) * 10, 30) : 0;
        $score += $activityScore;
        
        return round($score, 2);
    }

    /**
     * 生成追踪ID
     */
    private function generateTrackingId($appkey, $fingerprint)
    {
        return md5($appkey . $fingerprint . microtime(true) . rand(1000, 9999));
    }

    /**
     * 生成会话ID
     */
    private function generateSessionId()
    {
        return md5(microtime(true) . rand(1000, 9999));
    }

    /**
     * 生成安装ID
     */
    private function generateInstallId($appkey, $deviceId)
    {
        return md5($appkey . $deviceId . microtime(true));
    }

    /**
     * 生成事件ID
     */
    private function generateEventId($appkey, $fingerprint, $eventType)
    {
        return md5($appkey . $fingerprint . $eventType . microtime(true));
    }

    /**
     * 获取客户端IP
     */
    private function getClientIp()
    {
        $ipKeys = ['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'];
        foreach ($ipKeys as $key) {
            if (!empty($_SERVER[$key])) {
                $ips = explode(',', $_SERVER[$key]);
                return trim($ips[0]);
            }
        }
        return '0.0.0.0';
    }
}